package org.abratuhi.kvngroupware.server.db;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Properties;

import org.abratuhi.kvngroupware.brainstorm.model.Brainstorm;
import org.abratuhi.kvngroupware.jokemanager.model.Joke;
import org.abratuhi.kvngroupware.scenario.model.Scenario;
import org.abratuhi.kvngroupware.server.model.KVNGroupwareServer;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Database;
import org.xmldb.api.base.ResourceIterator;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.CollectionManagementService;
import org.xmldb.api.modules.XMLResource;
import org.xmldb.api.modules.XPathQueryService;

public class ServerDB {
	
	String root_collection = "xmldb:exist://localhost:8080/exist/xmlrpc/db";
	String root_user = "admin";
	String root_password = "rondalfa";
	
	String kvn_collection = "xmldb:exist://localhost:8080/exist/xmlrpc/db/kvn";
	String kvn_user = "kvnuser";
	String kvn_password = "kvn";
	
	KVNGroupwareServer kvnserver;
	
	public ServerDB(KVNGroupwareServer kvnserver, Properties props){
		this.kvnserver = kvnserver;
		root_collection = props.getProperty("exist.uri");
		root_user = props.getProperty("exist.user");
		root_password = props.getProperty("exist.password");
		kvn_collection = props.getProperty("exist.kvn.uri");
		kvn_user = props.getProperty("exist.kvn.user");
		kvn_password = props.getProperty("exist.kvn.password");
	}
	
	public boolean containsLogin(String id){
		boolean result = false;
		String query = "collection('/db/kvn')//users//user[@id='"+id+"']";
		ResourceSet checkedLogins = executeQuery(kvn_collection, kvn_user, kvn_password, query);
		try {
			result = (checkedLogins.getSize()>0)? true : false;
		} catch (XMLDBException e) {
			e.printStackTrace();
		}
		return result;
	}
	public boolean containsLoginPassword(String id, String password){
		boolean result = false;
		String query = "collection('/db/kvn')//users/user[@password='"+password+"'][@id='"+id+"']";
		ResourceSet checkedLogins = executeQuery(kvn_collection, kvn_user, kvn_password, query);
		try {
			result = (checkedLogins.getSize()>0)? true : false;
		} catch (XMLDBException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public Element getUser(String id){
		String query = "collection('/db/kvn')//users/user[@id='"+id+"']";
		ResourceSet checkedLogins = executeQuery(kvn_collection, kvn_user, kvn_password, query);
		return convertResourceSetToElements(checkedLogins)[0];
	}
	
	public void addUser(Element user){
		String query = "update insert "+convertElementToString(user)+" into collection('/db/kvn')//users";
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	
	public void addTeam(Element team){
		String query = "update insert "+convertElementToString(team)+" into collection('/db/kvn')//teams";
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
		
		String teamId = team.getAttributeValue("id");
		addCollectionAndFiles(kvn_collection, teamId, new String[]{"jokes.xml", "brainstorms.xml", "scenarios.xml"}, new String[]{"<jokes/>", "<brainstorms/>", "<scenarios/>"},  kvn_user, kvn_password);
	}
	public Element getTeam(String id){
		String query = "collection('/db/kvn')//teams/team[@id='"+id+"']";
		ResourceSet teams = executeQuery(kvn_collection, kvn_user, kvn_password, query);
		return convertResourceSetToElements(teams)[0];
	}
	public boolean containsTeam(String id){
		String query = "collection('/db/kvn')//teams/team[@id='"+id+"']";
		ResourceSet teams = executeQuery(kvn_collection, kvn_user, kvn_password, query);
		return (convertResourceSetToElements(teams).length>0);
	}
	public Element[] getTeams(){
		System.out.println("TEAMDB [get_teams]: ");
		String query = "collection('/db/kvn')//teams/team";
		ResourceSet teams = executeQuery(kvn_collection, kvn_user, kvn_password, query);
		return convertResourceSetToElements(teams);
	}
	
	public void addJoke(Element joke){
		String teamId = Joke.fromElement(joke).getTeamId();
		String query = "update insert "+convertElementToString(joke)+" into collection('/db/kvn/"+teamId+"')//jokes";
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	public void updateJoke(Element joke){
		String jid = joke.getAttributeValue("id");
		String jteam = joke.getAttributeValue("teamId");
		String query = "update replace collection('/db/kvn/"+jteam+"')//jokes/joke[@id='"+jid+"'] with "+convertElementToString(joke);
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	public void removeJoke(Element joke){
		String jid = joke.getAttributeValue("id");
		String jteam = joke.getAttributeValue("teamId");
		String query = "update delete collection('/db/kvn/"+jteam+"')//jokes/joke[@id='"+jid+"']";
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	public Element[] getJokes(String userId, String teamId){
		String query = "collection('/db/kvn/"+(teamId.equals("*")?"":teamId)+"')//jokes/joke";
		if(!userId.equals("*")) query += "[@authorId='"+userId+"']";
		if(!teamId.equals("*")) query += "[@teamId='"+teamId+"']";
		ResourceSet jokes = executeQuery(kvn_collection, kvn_user, kvn_password, query);
		return convertResourceSetToElements(jokes);
	}
	
	public void addScenario(Element scenario){
		String teamId = Scenario.fromElement(scenario).getTeamId();
		String query = "update insert "+convertElementToString(scenario)+" into collection('/db/kvn/"+teamId+"')//scenarios";
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	public Element[] getScenarios(String teamId){
		String query = "collection('/db/kvn/"+(teamId.equals("*")?"":teamId)+"')//scenarios/scenario";
		if(!teamId.equals("*")) query += "[@teamId='"+teamId+"']";
		ResourceSet scenarios = executeQuery(kvn_collection, kvn_user, kvn_password, query);
		return convertResourceSetToElements(scenarios);
	}
	public void updateScenario(Element eScenario){
		String sid = eScenario.getAttributeValue("id");
		String jteam = eScenario.getAttributeValue("teamId");
		String query = "update replace collection('/db/kvn/"+jteam+"')//jokes/joke[@id='"+sid+"'] with "+convertElementToString(eScenario);
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	public void removeScenario(String teamId, String scenarioId){
		String query = "update delete collection('/db/kvn/"+teamId+"')//scenarios/scenario[@id='"+scenarioId+"']";
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	public void addScenarioJoke(String scenarioId, String jokeId){
		String query = "update insert <joke id=\""+jokeId+"\"/> into collection('/db/kvn/')//scenarios/scenario[@id='"+scenarioId+"']";
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	public void removeScenarioJoke(String scenarioId, String jokeId){
		String query = "update delete collection('/db/kvn')//scenarios/scenario[@id='"+scenarioId+"']/joke[@id='"+jokeId+"']";
		executeQuery(query);
	}
	public void upScenarioJoke(String scenarioId, String jokeId){	//TODO
		
	}
	public void downScenarioJoke(String scenarioId, String jokeId){	//TODO
		
	}
	
	public void addBrainstorm(Element brainstorm){	// TODO
		String teamId = Brainstorm.fromElement(brainstorm).getTeamId();
		String query = "update insert "+convertElementToString(brainstorm)+" into collection('/db/kvn/"+teamId+"')//brainstorms";
		executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	public Element[] getBrainstorm(){	// TODO
		return null;
	} 
	
	
	
	@SuppressWarnings("unchecked")
	public ResourceSet executeQuery(String collection, String user, String password, String query){
		try {
			Class cl = Class.forName("org.exist.xmldb.DatabaseImpl");
			DatabaseManager.registerDatabase((Database)cl.newInstance());

			Collection dbCollection = DatabaseManager.getCollection(collection, user, password);
			
			if(dbCollection == null && collection.equals(kvn_collection)){
				initKVNDbContext();
				dbCollection = DatabaseManager.getCollection(collection, user, password);
			}
			
			XPathQueryService xPathService = (XPathQueryService) dbCollection.getService("XPathQueryService", "1.0");  
			xPathService.setProperty("indent", "yes");

			ResourceSet result = xPathService.query(query);
			return result;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (XMLDBException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public ResourceSet executeQuery(String query){
		return executeQuery(kvn_collection, kvn_user, kvn_password, query);
	}
	
	public void initKVNDbContext(){
		addCollectionAndFiles(root_collection, "kvn", new String[]{"users.xml", "teams.xml"}, new String[]{"<users/>", "<teams/>"},  kvn_user, kvn_password);
	}
	
	@SuppressWarnings("unchecked")
	public void addCollectionAndFiles(String rootCollection, String newCollection, String[] files, String user, String password){
		try {
			Class cl = Class.forName("org.exist.xmldb.DatabaseImpl");
			DatabaseManager.registerDatabase((Database)cl.newInstance());

			Collection rootDbCollection = DatabaseManager.getCollection(rootCollection, user, password);
			
			CollectionManagementService mgtService = (CollectionManagementService) rootDbCollection.getService("CollectionManagementService", "1.0");
			
			Collection newDbCollection = mgtService.createCollection(newCollection);
			
			for(int i=0; i<files.length; i++){
				newDbCollection.storeResource(newDbCollection.createResource(files[i], "XMLResource"));
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (XMLDBException e) {
			e.printStackTrace();
		}
	}
	
	@SuppressWarnings("unchecked")
	public void addCollectionAndFiles(String rootCollection, String newCollection, String[] files, String[] contents, String user, String password){
		try {
			Class cl = Class.forName("org.exist.xmldb.DatabaseImpl");
			DatabaseManager.registerDatabase((Database)cl.newInstance());

			Collection rootDbCollection = DatabaseManager.getCollection(rootCollection, user, password);
			
			CollectionManagementService mgtService = (CollectionManagementService) rootDbCollection.getService("CollectionManagementService", "1.0");
			
			Collection newDbCollection = mgtService.createCollection(newCollection);
			
			for(int i=0; i<files.length; i++){
				XMLResource newDbResource = (XMLResource) newDbCollection.createResource(files[i], "XMLResource");
				newDbResource.setContent(contents[i]);
				newDbCollection.storeResource(newDbResource);
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (XMLDBException e) {
			e.printStackTrace();
		}
	}
	
	public Element convertXMLResourceToElement(XMLResource xmlresource){
		Element element = null;
		SAXBuilder saxbuilder = new SAXBuilder();
		try {
			element = (Element) saxbuilder.build(new StringReader((String) xmlresource.getContent())).getRootElement().detach();
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (XMLDBException e) {
			e.printStackTrace();
		}
		return element;
	}
	public Element[] convertResourceSetToElements(ResourceSet resourceset){
		ArrayList<Element> elements = new ArrayList<Element>();
		try {
			ResourceIterator iterator = resourceset.getIterator();
			while(iterator.hasMoreResources()){
				XMLResource resource = (XMLResource) iterator.nextResource();
				Element element = convertXMLResourceToElement(resource);
				if(element != null) elements.add(element);
			}
		} catch (XMLDBException e) {
			e.printStackTrace();
		}
		return elements.toArray(new Element[elements.size()]);
	}
	
	public String convertElementToString(Element element){
		XMLOutputter xmlout = new XMLOutputter();
		StringWriter sw = new StringWriter();
		try {
			xmlout.output(element, sw);
			return sw.toString();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

}
